import React from 'react';
import './App.css';
import * as _r from './Core/Core';
import { Stage, Layer, Group, Line, Rect } from 'react-konva';
import GlobalStyle, { Color } from './Global'

export interface BoardEventHandlers {
    onPointClick?: (coord: _r.Coord) => void;
    onMirrorClick?: (coord: _r.Coord, mirror: _r.TypeOfMirror) => void;
}

export class BoardCommonStyle {
    constructor(Global = new GlobalStyle()) {
        this.global = Global;
    }
    global: GlobalStyle;
    gridWidth = 30;
    gridHeight = 30;
}

export class MirrorStyle {
    constructor(Global = new GlobalStyle(), Board = new BoardCommonStyle(Global)) {
        this.global = Global;
        this.board = Board;
    }
    global: GlobalStyle;
    board: BoardCommonStyle;
    mirrorWidth = 2;
}

export class PointStyle {
    constructor(Global = new GlobalStyle(), Board = new BoardCommonStyle()) {
        this.global = Global;
        this.board = Board;
    }
    global: GlobalStyle;
    board: BoardCommonStyle;
    size = 4;
    touchSize = 15;
    hoverSize = 8;
    color = "grey";
    hoverColor = "yellow";
    selectedSize = 6;
    selectedColor = "red";
}

export class HomeStyle {
    constructor(Global = new GlobalStyle(), Board = new BoardCommonStyle()) {
        this.global = Global;
        this.board = Board;
    }
    global: GlobalStyle;
    board: BoardCommonStyle;
    size = 4;
}

export class BoardStyle {
    constructor(Global = new GlobalStyle()) {
        this.global = Global;
        this.common = new BoardCommonStyle(Global);
        this.mirror = new MirrorStyle(Global, this.common);
        this.point = new PointStyle(Global, this.common);
        this.home = new HomeStyle(Global, this.common);
    }
    global: GlobalStyle;
    common: BoardCommonStyle;
    mirror: MirrorStyle;
    point: PointStyle;
    home: HomeStyle;
}

export class Mirror extends React.Component<
    {
        data: _r.BorderMirrorType | _r.CrossMirrorType,
        type: _r.TypeOfMirror,
        coord: _r.Coord,
        style: MirrorStyle,
        eventHandlers: BoardEventHandlers
    }>
{
    render() {
        let w = this.props.style.board.gridWidth;
        let h = this.props.style.board.gridHeight;
        let lt = [0, 0];
        let rt = [w, 0];
        let lb = [0, h];
        let rb = [w, h];
        let points: number[] = [];
        switch (this.props.type) {
            case _r.TypeOfMirror.Left:
                points = lt.concat(lb);
                break;
            case _r.TypeOfMirror.Right:
                points = rt.concat(rb);
                break;
            case _r.TypeOfMirror.Top:
                points = lt.concat(rt);
                break;
            case _r.TypeOfMirror.Bottom:
                points = lb.concat(rb);
                break;
            case _r.TypeOfMirror.Slash:
                points = rt.concat(lb);
                break;
            case _r.TypeOfMirror.BackSlash:
                points = lt.concat(rb);
                break;
        }
        let color: Color;
        if (this.props.type === _r.TypeOfMirror.Slash || this.props.type === _r.TypeOfMirror.BackSlash) {
            let cdata = this.props.data as _r.CrossMirrorType;
            if (_r.TypeOfCross2TypeOfMirror(cdata.Type) !== this.props.type) {
                color = this.props.style.global.colorOfPlayer.get(_r.Player.None);
            } else {
                color = this.props.style.global.colorOfPlayer.get(cdata.Whose);
            }
        } else {
            let bdata = this.props.data as _r.BorderMirrorType;
            color = this.props.style.global.colorOfPlayer.get(bdata.Whose);
        }
        return (
            <Line
                x={this.props.coord.x * w}
                y={this.props.coord.y * h}
                points={points}
                stroke={color}
                strokeWidth={this.props.style.mirrorWidth}
                onclick={() => {
                    if (this.props.eventHandlers.onMirrorClick) {
                        this.props.eventHandlers.onMirrorClick(this.props.coord, this.props.type);
                    }
                }} />
        )
    }
}

//<Rect
//    x={(l + r) / 2}
//    y={(b + t) / 2}
//    offsetX={this.props.style.homeSize / 2}
//    offsetY={this.props.style.homeSize / 2}
//    width={this.props.style.homeSize}
//    height={this.props.style.homeSize}
//    fill={ColorHome}
///>

export enum PointStatus {
    Normal = 0,
    Hover = 1,
    Selected = 2
}

export class Point extends React.Component<
    {
        coord: _r.Coord,
        style: PointStyle,
        eventHandlers: BoardEventHandlers
    },
    {
        status: PointStatus
    }>
{
    constructor(props: any) {
        super(props);
        this.state = {
            status: PointStatus.Normal
        };
    }
    mouseEnterHandler = () => {
        this.setState({
            status: this.state.status | PointStatus.Hover
        });
    }
    mouseLeaveHandler = () => {
        this.setState({
            status: this.state.status & ~PointStatus.Hover
        });
    }
    render() {
        let x = this.props.coord.x * this.props.style.board.gridWidth;
        let y = this.props.coord.y * this.props.style.board.gridHeight;
        let hoverSize = this.props.style.hoverSize;
        let hoverColor = this.state.status & PointStatus.Hover ? this.props.style.hoverColor : "transparent";
        let selectedSize = this.props.style.selectedSize;
        let selectedColor = this.state.status & PointStatus.Selected ? this.props.style.selectedColor : "transparent";
        let size = this.props.style.size;
        let color = this.props.style.color;
        let touchSize = this.props.style.touchSize;
        return (
            <Group
                x={x}
                y={y}>
                <Rect  /*hover*/
                    width={hoverSize}
                    height={hoverSize}
                    offsetX={hoverSize / 2}
                    offsetY={hoverSize / 2}
                    fill={hoverColor} />
                <Rect  /*selected*/
                    width={selectedSize}
                    height={selectedSize}
                    offsetX={selectedSize / 2}
                    offsetY={selectedSize / 2}
                    fill={selectedColor} />
                <Rect /*main*/
                    width={size}
                    height={size}
                    offsetX={size / 2}
                    offsetY={size / 2}
                    fill={color} />
                <Rect /*touch*/
                    width={touchSize}
                    height={touchSize}
                    offsetX={touchSize / 2}
                    offsetY={touchSize / 2}
                    fill="transparent"
                    onMouseEnter={this.mouseEnterHandler}
                    onMouseLeave={this.mouseLeaveHandler}
                    onClick={() => {
                        if (this.props.eventHandlers.onPointClick) {
                            this.props.eventHandlers.onPointClick(this.props.coord);
                        }
                    }} />
            </Group>
        );
    }
}

export class Home extends React.Component<
    {
        coord: _r.Coord,
        style: HomeStyle,
        eventHandlers: BoardEventHandlers,
        data: _r.GridHome
    }>
{
    render() {
        let x = this.props.coord.x * this.props.style.board.gridWidth + this.props.style.board.gridWidth / 2;
        let y = this.props.coord.y * this.props.style.board.gridHeight + this.props.style.board.gridHeight / 2;
        let size = this.props.style.size;
        let color = this.props.style.global.colorOfPlayer.get(this.props.data.Whose);
        return (
            <Group
                x={x}
                y={y}>
                <Rect /*main*/
                    width={size}
                    height={size}
                    offsetX={size / 2}
                    offsetY={size / 2}
                    fill={color}
                />
            </Group>
        );
    }
}
export default class Board extends React.Component<
    {
        data: _r.Board,
        style: BoardStyle,
        eventHandlers: BoardEventHandlers
    }>
{
    constructor(props: any) {
        super(props);
        for (let y = 0; y <= this.props.data.Ny; y++) {
            this.refPoints.push([]);
            for (let x = 0; x <= this.props.data.Nx; x++) {
                this.refPoints[y].push(React.createRef<Point>());
            }
        }
        for (let y = 0; y <= this.props.data.Ny; y++) {
            this.refMirrors.push([]);
            for (let x = 0; x <= this.props.data.Nx; x++) {
                this.refMirrors[y].push([]);
                _r.AllTypesOfMirror.forEach(() => {
                    this.refMirrors[y][x].push(React.createRef<Mirror>());
                })
            }
        }
    }
    refPoints: React.RefObject<Point>[][] = [[]];
    refMirrors: React.RefObject<Mirror>[][][] = [[[]]];
    selectedPoint = new _r.Coord();
    selectPoint(coord: _r.Coord) {
        let ref = this.refPoints[coord.y][coord.x];
        if (ref === null) {
            throw new Error("refPoint is null");
        } else {
            ref.current?.setState({
                status: ref.current.state.status | PointStatus.Selected
            });
        }
    }
    unSelectPoint(coord: _r.Coord) {
        let ref = this.refPoints[coord.y][coord.x];
        if (ref === null) {
            throw new Error("refPoint is null");
        } else {
            ref.current?.setState({
                status: ref.current.state.status & ~PointStatus.Selected
            });
        }
    }
    renderMirrors() {
        let mirrors: JSX.Element[] = [];
        this.props.data.Data.forEach((row, y) => {
            row.forEach((grid, x) => {
                if (grid.Type === _r.TypeOfGrid.Normal) {
                    _r.AllTypesOfMirror.forEach((type) => {
                        mirrors.push(<Mirror
                            key={"Grid" + (y * this.props.data.Nx + x) + "Mirror" + type}
                            data={(grid as _r.GridNormal).Mirror.Get(type)}
                            type={type}
                            coord={new _r.Coord(x, y)}
                            style={this.props.style.mirror}
                            eventHandlers={this.props.eventHandlers}
                            ref={this.refMirrors[y][x][type]} />);
                    });
                }
            });
        });
        return mirrors;
    }
    renderPoints() {
        let points: JSX.Element[] = [];
        for (let y = 0; y <= this.props.data.Ny; y++) {
            for (let x = 0; x <= this.props.data.Nx; x++) {
                points.push(<Point
                    key={"Point" + y * this.props.data.Nx + x}
                    coord={new _r.Coord(x, y)}
                    style={this.props.style.point}
                    eventHandlers={this.props.eventHandlers}
                    ref={this.refPoints[y][x]} />);
            }
        }
        return points;
    }
    renderHomes() {
        let c1 = this.props.data.GetHomeCoord(_r.Player.P1);
        let c2 = this.props.data.GetHomeCoord(_r.Player.P2);
        return <>
            {c1 !== _r.InvalidCoord ?
                <Home
                    coord={c1}
                    data={this.props.data.GetGrid(c1.x, c1.y) as _r.GridHome}
                    eventHandlers={this.props.eventHandlers}
                    style={this.props.style.home} />
                : <></>
            }
            {c2 !== _r.InvalidCoord ?
                <Home
                    coord={c2}
                    data={this.props.data.GetGrid(c2.x, c2.y) as _r.GridHome}
                    eventHandlers={this.props.eventHandlers}
                    style={this.props.style.home} />
                : <></>
            }
        </>
    }
    render() {
        let margin = 5;
        let innerMargin = 3;
        let marginProps = {
            x: innerMargin,
            y: innerMargin
        };
        console.log("ReRender!!!");
        return (
            <Stage
                style={{ margin: margin }}
                width={this.props.data.Nx * this.props.style.common.gridWidth + innerMargin * 2}
                height={this.props.data.Ny * this.props.style.common.gridHeight + innerMargin * 2}>
                <Layer {...marginProps}>{this.renderMirrors()}</Layer>
                <Layer {...marginProps}>{this.renderPoints()}</Layer>
                <Layer {...marginProps}>{this.renderHomes()}</Layer>
            </Stage>
        );
    }
}
